package FileStructure;

import Tools.cons;
import Tools.debug;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class BitField {
    public int numPieces;           // Banyaknya pieces
    public byte[] byteField;        // BitField dalam bentuk array of byte
    public boolean[] boolField;     // BitField dalam bentuk array of boolean

    public BitField(){
        
    }

    public BitField(BitField bf) {
        this.numPieces = bf.numPieces;
        this.byteField = bf.byteField;
        this.boolField = bf.boolField;
    }

    public BitField(String dir, String sharedFileName, String torrentFileName) throws IOException {
        // Menghasilkan nilai BitField dari pembacaan file asli
        this.numPieces = getNumPiecesFromFile(dir, sharedFileName);
        // Mengisi boolField
        FileHandler sharedFH = new FileHandler();
        sharedFH.hashFile(dir, sharedFileName);         // Mendapatkan nilai hash dari file asli
        FileHandler torrentFH = new FileHandler();
        torrentFH.readTorrent(dir, torrentFileName);    // Mendapatkan nilai hash dari file torrent
        // Membandingkan kedua nilai hash
        if (torrentFH.piecesHashVal.size() == this.numPieces) {
            this.boolField = new boolean[this.numPieces];
            for (int i=0; i<this.numPieces; i++) {
                this.boolField[i] = torrentFH.piecesHashVal.get(i).equals(sharedFH.piecesHashVal.get(i));
            }
        } else {
            // Jumlah piece berbeda, kemungkinan file yang di-hash berbeda
            System.out.println("File torrent yang dipilih salah");
        }
        this.generateByteField();
    }

    public BitField(byte[] bytes, String dir, String torrentFileName) throws FileNotFoundException {
        // Menghasilkan nilai BitField dari bytes yang diterima
        this.byteField = bytes;
        this.numPieces = getNumPiecesFromTorrent(dir, torrentFileName);
        this.generateBoolField();
    }

    public final void generateBoolField() {
        // Generate boolField dari byteField
        // numPieces diketahui
        this.boolField = new boolean[this.numPieces];
        for (int i=0; i<this.numPieces; i++) {
            this.boolField[i] = getBit(this.byteField, i);
        }
    }

    public final void generateByteField() {
        // Generate ByteField dari boolField
        // numPieces diketahui
        this.byteField = new byte[this.numPieces / 8 + ((this.numPieces%8)==0 ? 0 : 1)];
        for (int i=0; i<this.byteField.length; i++) {
            for (int j=0; j<8; j++) {
                this.byteField[i] <<= 1;
                if (((i*8+j)<this.boolField.length) && (this.boolField[i*8+j])) {
                    this.byteField[i] += 1;
                }
            }
        }
    }

    public static int getNumPiecesFromFile(String dir, String sharedFileName) {
        // Mendapatkan NumPieces dari pembacaan file asli
        File f = new File(dir + sharedFileName);
        return (int) (f.length() / cons.pieceLength) + ((f.length()%cons.pieceLength)==0 ? 0 : 1);
    }

    public static int getNumPiecesFromTorrent(String dir, String torrentFileName) throws FileNotFoundException {
        // Mendapatkan NumPieces dari pembacaan file torrent
        FileHandler fh = new FileHandler();
        fh.readTorrent(dir, torrentFileName);
        return (int) (fh.fileLength / cons.pieceLength)  + ((fh.fileLength%cons.pieceLength)==0 ? 0 : 1);
    }

    public static boolean getBit(byte[] data, int pos) {
        int posByte = pos / 8;
        int posBit = pos % 8;
        byte valByte = data[posByte];
        int valInt = valByte >> (8 - (posBit + 1)) & 0x0001;
        return (valInt == 1);
    }

    public void PrintBytes() {
        for (byte b : this.byteField) {
            System.out.println((int) b);
        }
    }

    public void PrintBooleans() {
        for (boolean bool : this.boolField) {
            if (bool)
                System.out.print("1");
            else
                System.out.print("0");
        }
        System.out.println();
    }

    public int[] getListPieceIndex() {
        int counter = 0;
        for (boolean bool : this.boolField) {
            if (bool)
                ++counter;
        }
        int[] ints = new int[counter];
        counter = 0;
        for (int i=0; i<this.boolField.length; ++i) {
            if (this.boolField[i]) {
                ints[counter] = i;
                ++counter;
            }
        }
        return ints;
    }

    public static void main(String[] args) throws Exception {
        String dir = "test/";
		String sharedFile = "laguku.mp3";
		String torrentFile = "laguku.mp3.torrent";
        System.out.println(getNumPiecesFromFile(dir, sharedFile));
        System.out.println(getNumPiecesFromTorrent(dir, torrentFile));

        byte[] bytes = new byte[2];
        bytes[0] = (byte) -1;
        bytes[1] = (byte) 0;
        BitField bf = new BitField(bytes, dir, torrentFile);
        bf.PrintBytes();
        bf.PrintBooleans();

        //bf = new BitField(dir, sharedFile, torrentFile);
        bf.PrintBooleans();
        //bf.PrintBytes();
        int[] ints = bf.getListPieceIndex();
        for (int i : ints) {
            System.out.println(i);
        }
    }
}
